package client.view;

import client.comms.PostData;
import client.model.Genre;
import client.model.Track;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.text.*;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * Created by IntelliJ IDEA.
 * User: sam
 * Date: 16/02/12
 * Time: 06:20
 * To change this template use File | Settings | File Templates.
 */
public class DownloadControlPanel extends JPanel implements ActionListener {

    ArrayList genreData;
    DefaultTableModel tableModel;   //populated with tableModelData
    Vector columns; // core data, standalone from table and table model (holds Genre objects)
    JTable jTable;

    Vector chosenGenres;

    DownloadResultsPanel downloadResultsPanel;
    UploadPanel uploadPanel;
    JEditorPane outputTextArea;

    JTextField trackSearchField;
    JTextField artistSearchField;

    public DownloadControlPanel() {
    }

    private void buildGenreTable() {
        Vector<Vector<String>> modelData = new Vector<Vector<String>>();
        tableModel = new DefaultTableModel(modelData, columns);
    }

    public ArrayList populateGenreTable(Document genreResponse) {

        Vector<Vector<String>> modelData = new Vector<Vector<String>>();

        NodeList nList = genreResponse.getElementsByTagName("Response");
        Node nNode = nList.item(0);
        Element eElement = (Element) nNode;
        String type = eElement.getAttribute("type");
        nList = genreResponse.getElementsByTagName("Genre");

        if(type.equals("Error")) {
            String errorCode = eElement.getAttribute("code");

            if(errorCode.equals("MF_SESSION_EXPIRED")) {
                JOptionPane.showMessageDialog(this,"Refresh Failed. Session has expired. Please login.", "Error", JOptionPane.ERROR_MESSAGE);
            }
        } else {

            genreData = new ArrayList(nList.getLength());

            for (int i = 0; i < nList.getLength(); i++) {
                nNode = nList.item(i);
                if (nNode.getNodeType() == Node.ELEMENT_NODE) {
                    eElement = (Element) nNode;
                    eElement.normalize();
                    String name = eElement.getAttribute("name");
                    String num_entries = eElement.getAttribute("num_entries");
                    String updated = eElement.getAttribute("updated");
                    String description = eElement.getAttribute("description");

                    ReportsPanel.log(name + ", "+num_entries+", "+updated+", "+description);

                    Genre genre = new Genre(name, num_entries, updated, description);
                    genreData.add(genre);

                    Vector <String> subData = new Vector<String>();
                    subData.add(genre.getName());
                    subData.add(genre.getNumEntries());
                    subData.add(genre.getUpdated());

                    modelData.add(subData);  // important that we add data Vector at same index as genreData ArrayList
                    }
                }

            if(tableModel == null) {
                tableModel = new DefaultTableModel(modelData, columns);
            } else {
                tableModel.setDataVector(modelData, columns);
            }

            jTable.getColumnModel().getColumn(0).setMaxWidth(135);
            jTable.getColumnModel().getColumn(1).setMaxWidth(50);
            jTable.getColumnModel().getColumn(2).setMaxWidth(115);

            // TODO is this okay? why didnt i have this here before?
            // important cos need to update this after login response with genres
            // and dont have direct access to it
            uploadPanel.updateGenreList(genreData);

            return genreData;
        }

        return new ArrayList(0);
    }

    public ArrayList getGenreData() {
        return genreData;
    }


    public void init(DownloadResultsPanel downloadResultsPanel, UploadPanel uploadPanel) {

        this.downloadResultsPanel = downloadResultsPanel;
        this.uploadPanel = uploadPanel;

        setLayout(new FlowLayout(FlowLayout.LEFT,0,0));

            Box box = Box.createVerticalBox();

            // tempDataModel properties
            //Vector<Vector<String>> modelData = new Vector<Vector<String>>();
            columns = new Vector();

            columns.add("Catagory");
            columns.add("Tracks");
            columns.add("Updated");

            //final JTable jTable = new JTable(); // referenced from within inner class, so need "final"

            //Vector<Vector<String>> modelData = populateGenreTable(genreResponse);

        /**
         * <MFResponse>
                <Response type='Catagories'>
                    <Genre name='Rock' num_entries='324' updated='02/25/2012' description='American pop, British rock, etc'/>
                </Response>
          </MFResponse>
         */
              buildGenreTable();
            //populateGenreTable(genreResponse);

            //tableModel.setDataVector(modelData, columns);
            //jTable.setModel(tableModel);

            //uploadPanel.updateGenreList(genreData);

            //tableModel = new DefaultTableModel(modelData, columns);

            jTable = new JTable(tableModel);

            //final JTable jTable = new JTable(tableModel); // referenced from within inner class, so need "final"
            //jTable.setModel(tableModel);

            JPanel tableContainer = new JPanel();

            JScrollPane scrollPane = new JScrollPane(jTable);

            DefaultTableCellRenderer rightRenderer = new DefaultTableCellRenderer();
            rightRenderer.setHorizontalAlignment(SwingConstants.CENTER);
            //jTable.getColumnModel().getColumn(0).setCellRenderer(rightRenderer);
            jTable.getColumnModel().getColumn(1).setCellRenderer(rightRenderer);
            jTable.getColumnModel().getColumn(2).setCellRenderer(rightRenderer);

            //jTable.getColumnModel().getColumn(0).setPreferredWidth(jTable.getColumnModel().getColumn(0).getPreferredWidth());
            //jTable.getColumnModel().getColumn(1).setPreferredWidth(jTable.getColumnModel().getColumn(1).getPreferredWidth());
            //jTable.getColumnModel().getColumn(2).setPreferredWidth(jTable.getColumnModel().getColumn(2).getPreferredWidth());

            jTable.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN); //JTable.AUTO_RESIZE_OFF

            //double width = getMinimumSize().getWidth();
            //Dimension d = new Dimension ((int) width, 250);


            DefaultTableModel tableModel = (DefaultTableModel) jTable.getModel();

            Vector<Vector<String>> modelData = tableModel.getDataVector();

            //Dimension d = new Dimension(((int) scrollPane.getPreferredSize().getWidth()), jTable.getRowHeight()*data.length);
            Dimension d = new Dimension(290, jTable.getRowHeight()*7);
            scrollPane.setPreferredSize(d);  // forces vertical scroll bar

            DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer();
            centerRenderer.setHorizontalAlignment( JLabel.CENTER );
            //jTable.setCellRenderer(centerRenderer);

            //jTable.setFillsViewportHeight(true);
            //jTable.setLayout(new BorderLayout());
            //jTable.add(table.getTableHeader(), BorderLayout.PAGE_START);
            //jTable.add(table, BorderLayout.CENTER);
            //JPanel resultPanel = new JPanel();
            //resultPanel.setLayout( new GridLayout( 3, categories.length ) );
            //GridBagConstraints gbc = new GridBagConstraints();
            //gbc.anchor = GridBagConstraints.NORTH;
            //box.add(Box.createRigidArea(new Dimension(0,5)));

            box.add(scrollPane);
            box.add(Box.createRigidArea(new Dimension(10, 7)));

            //Dimension d = new Dimension(300,((int)scrollPane.getViewport().getViewSize().getHeight()));
            //scrollPane.setPreferredSize(d);
            //box.add(Box.createVerticalStrut(20));

            JPanel searchPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT,0,0));
            JPanel trackPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT,0,0));
            JPanel artistPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT,0,0));

            searchPanel.setLayout(new GridLayout(3, 1, 0, 0));

        Border bevelBorder = BorderFactory.createBevelBorder(BevelBorder.LOWERED);
        //Border lineBorder = BorderFactory.createEtchedBorder();
        //Border lowerBevelBorder = BorderFactory.createLoweredBevelBorder();

            trackSearchField = new JTextField("");
            trackSearchField.setBorder(bevelBorder);
            Dimension d1 = new Dimension(245, 20);
            trackSearchField.setPreferredSize(d1);
            JLabel trackLabel = new JLabel("Track ");

            trackPanel.add(trackLabel);
            trackPanel.add(trackSearchField);

            artistSearchField = new JTextField("");
            artistSearchField.setBorder(bevelBorder);
            JLabel artistLabel = new JLabel("Artist ");

            artistSearchField.setPreferredSize(d1);
            artistPanel.add(artistLabel);
            artistPanel.add(artistSearchField);

            searchPanel.add(trackPanel);
            searchPanel.add(artistPanel);

            JButton searchButton = new JButton("Find");
            JButton refreshButton = new JButton("Refresh");

            JPanel outputPanel = new JPanel();

            // http://explodingpixels.wordpress.com/2008/10/28/make-jeditorpane-use-the-system-font/

            outputTextArea = new JEditorPane();//JTextArea();
            // TODO outputTextArea needs to grow / shrink width

            outputTextArea.setEditable(false);
            outputTextArea.setContentType("text/html");
            //outputTextArea.setBackground(new Color(255,255,250));

            //StyledDocument doc = (StyledDocument) outputTextArea.getDocument();

            // Create a style object and then set the style attributes
            //Style style = doc.addStyle("StyleName", null);

            //StyleConstants.setBackground(style, new Color(10,10,255));

            outputTextArea.setPreferredSize(new Dimension(285, 100));
            //outputTextArea.setMinimumSize(new Dimension(50, 100));

            JScrollPane outScrollpane = new JScrollPane(outputTextArea);

            //outputTextArea.setWrapStyleWord(true);
            //outputTextArea.setLineWrap(true);
            //outputTextArea.setEditable(false);
            //outputTextArea.setFont(new Font("Serif", Font.BOLD, 10));

            //JScrollPane outputScrollPane = new JScrollPane(jTable);
            //outputScrollPane.setPreferredSize(new Dimension(285, 65));

            /*JTextPane outputTextPane = new JTextPane();
            outputTextPane.setPreferredSize(new Dimension(285, 65));

            Highlighter h = outputTextPane.getHighlighter();

            try {
                h.addHighlight(1, 10, new DefaultHighlighter.DefaultHighlightPainter(Color.red));
            } catch (BadLocationException e) {
                e.printStackTrace();
            }
            */

            //JTextField outputTextField = new JTextField();
            //outputTextField.setPreferredSize(new Dimension(285, 65));

            outputPanel.add(outScrollpane);

            try {
                URL resourceURL = getClass().getClassLoader().getResource(MFWindow.MEDIA+"folder_icon.png");
                Icon tmpIcon = new ImageIcon(resourceURL);
                searchButton.setIcon(tmpIcon);

                resourceURL = getClass().getClassLoader().getResource(MFWindow.MEDIA+"refresh_icon.png");
                tmpIcon = new ImageIcon(resourceURL);
                refreshButton.setIcon(tmpIcon);
            } catch (Exception e) {
               ReportsPanel.log("Error: "+e);
            }

            //ReportsPanel.log("getParent "+getParent());
            searchButton.addActionListener(this);
            refreshButton.addActionListener(this);

            JPanel btnPanel = new JPanel();
            btnPanel.setLayout(new GridLayout(1, 2, 5, 0));
            btnPanel.add(refreshButton);
            btnPanel.add(searchButton);
            searchPanel.add(btnPanel);
            box.add(searchPanel);
            box.add(outputPanel);

            //ImagePanel imgPanel = new ImagePanel((int)tableContainer.getPreferredSize().getWidth(), 84);
            //imgPanel.setLayout(new FlowLayout(FlowLayout.CENTER,0,10));
            box.add(Box.createRigidArea(new Dimension((int)tableContainer.getPreferredSize().getWidth(), 7)));
            //box.add(imgPanel);
            //box.add(Box.createRigidArea(new Dimension((int)tableContainer.getPreferredSize().getWidth(), 7)));
            tableContainer.add(box);

            //imgPanel.repaint();
            //box.repaint();

            add(tableContainer);

            jTable.addMouseListener(new MouseListener() {
                public void mouseClicked(MouseEvent mouseEvent) {

                    chosenGenres = new Vector();

                    int[] selectedRows = jTable.getSelectedRows();

                    outputTextArea.setText("");

                    HTMLDocument htmlDocument = new HTMLDocument();//(HTMLDocument) outputTextArea.getDocument();
                    HTMLEditorKit htmlEditorKit = new HTMLEditorKit(); //(HTMLEditorKit) outputTextArea.getEditorKit();

                    outputTextArea.setDocument(htmlDocument);
                    outputTextArea.setEditorKit(htmlEditorKit);

                    StringBuffer strHTMLBuffer = new StringBuffer();
                    strHTMLBuffer.append("<html><font size=2 face='Courier New' >");

                    for(int j = 0; j < selectedRows.length; j++) {
                        int selectedIndex = selectedRows[j];
                        System.out.println("selectedIndex "+selectedIndex);
                        String tmpChosenGenre = (String) jTable.getModel().getValueAt(selectedIndex, 0);
                        Genre genre = (Genre) genreData.get(selectedIndex+1); // +1 because  genreData[0] has "Genres..." as first item
                        System.out.println(genre.getName());

                        strHTMLBuffer.append("<b>Genre:&nbsp;&nbsp;</b>"+tmpChosenGenre+"<br>");
                        strHTMLBuffer.append("<b>Description:&nbsp;&nbsp;</b>"+genre.getDescription() +"<br><br>");

                        chosenGenres.add(tmpChosenGenre);
                    }

	                if(chosenGenres.size() > 0) {
		                searchGenre();
	                }

                    strHTMLBuffer.append("</font></html>");

                    outputTextArea.setText(strHTMLBuffer.toString());
                }

                public void mousePressed(MouseEvent mouseEvent) {}
                public void mouseReleased(MouseEvent mouseEvent) {}
                public void mouseEntered(MouseEvent mouseEvent) {}
                public void mouseExited(MouseEvent mouseEvent) {}
            });
    }


        public void setUploadPanel(UploadPanel uploadPanel) {

            ReportsPanel.log("setUploadPanel ");
            this.uploadPanel = uploadPanel;
        }

     private void sendSearchRequest(String userTrack, String userArtist) {

         StringBuilder strReq = new StringBuilder();

          // TODO order by attribute, so SQL can do order query, timestamp, alphabetical
                    strReq.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>"+"\n");
                    strReq.append("<MFRequest>"+"\n");
                    strReq.append("<Request type='Search' track_name='"+userTrack+"' artist_name='"+userArtist+"'/>"+"\n");

                    for (int i = 0; i < chosenGenres.size(); i++) {
                        strReq.append("<Genre name='"+chosenGenres.get(i)+"'/>"+"\n");
                    }

                    strReq.append("</MFRequest>"+"\n");

                    PostData postData = new PostData();
                    ReportsPanel.log(strReq.toString());
                    Document tracksResponse = postData.sendDataForWriteReponse(strReq.toString());


                    NodeList nList = tracksResponse.getElementsByTagName("Response");
                    Node nNode = nList.item(0);
                    Element eElement = (Element) nNode;
                    String type = eElement.getAttribute("type");

                /**
                 * <MFResponse>
                 *     <Response type='Error' code='MF_SESSION_EXPIRED'>
                 *         </Response>
                 * </MFResponse>
                 */

                if(type.equals("Error")) {
                    String errorCode = eElement.getAttribute("code");

                    if(errorCode.equals("MF_SESSION_EXPIRED")) {

                       JOptionPane.showMessageDialog(this,"Search Failed. Session has expired. Please login.", "Error", JOptionPane.ERROR_MESSAGE);
                    }
                } else {

                    nList = tracksResponse.getElementsByTagName("Track");

                    // tmp trackData make "copy" for results panel to store
                    ArrayList trackData = new ArrayList();

                    for (int i = 0; i < nList.getLength(); i++) {
                        nNode = nList.item(i);
                        if (nNode.getNodeType() == Node.ELEMENT_NODE) {
                            eElement = (Element) nNode;
                            eElement.normalize();
                            String trackName = eElement.getAttribute("name");
                            String artistName = eElement.getAttribute("artist");
                            String contributor = eElement.getAttribute("contributor");
                            String runTime = eElement.getAttribute("runTime");
                            String fileType = eElement.getAttribute("type");
                            String uploaded = eElement.getAttribute("uploaded");

                            Track track = new Track(trackName, artistName, contributor, runTime, fileType, uploaded);
                            trackData.add(track);
                        }
                    }

                    downloadResultsPanel.updateTracks(trackData);
                }
     }

        private void searchGenre() {

            String SANITIZE_DB_PATTERN = "^([a-zA-Z_\\-\\s0-9\\s]+)$";

            Pattern dbPattern = Pattern.compile(SANITIZE_DB_PATTERN, Pattern.CASE_INSENSITIVE);
            Matcher dbMatcher;

            ReportsPanel.log("DownloadControlPanel.searchGenre() ");

            if(chosenGenres != null && chosenGenres.size() > 0) {

                String userTrack = trackSearchField.getText();
                String userArtist = artistSearchField.getText();

                boolean inputError = false;

                if(!userTrack.trim().equals("")) {
                    dbMatcher = dbPattern.matcher(userTrack);
                    if(!dbMatcher.matches()) {
                        inputError = true;
                        JOptionPane.showMessageDialog(this, "Track search has illegal characters", "Error", JOptionPane.ERROR_MESSAGE);
                    }
                } else {
                    userTrack = userTrack.trim();
                }

                if(!userArtist.trim().equals("")) {

                    dbMatcher = dbPattern.matcher(userArtist);

                    if(!dbMatcher.matches() && !inputError) {
                        inputError = true;
                        JOptionPane.showMessageDialog(this, "Artist search has illegal characters", "Error", JOptionPane.ERROR_MESSAGE);
                    }
                } else {
                    userArtist = userArtist.trim();
                }

                if(!inputError ){
                    sendSearchRequest(userTrack, userArtist);
                }

            } else {
                JOptionPane.showMessageDialog(this,"Select one or more genres to search.", "Error", JOptionPane.ERROR_MESSAGE);
                //outputTextArea.setText("Select one or more genres to search.");
            }
        }

        private void getGenres() {
            StringBuilder strReq = new StringBuilder();

            strReq.append("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>"+"\n");
            strReq.append("<MFRequest>"+"\n");
            strReq.append("<Request type='GetCatagories' data=''/>"+"\n");
            strReq.append("</MFRequest>"+"\n");

            PostData postData = new PostData();
            Document genreResponse = postData.sendDataForWriteReponse(strReq.toString());

            ArrayList tmpList = populateGenreTable(genreResponse);

            if(tmpList.size() > 0) {  // this is done at end of  populateGenreTable() above
                uploadPanel.updateGenreList(genreData);
            }
        }

        public void actionPerformed(ActionEvent actionEvent) {

            String tmpAction = actionEvent.getActionCommand();

            ReportsPanel.log("actionPerformed "+tmpAction);

            if(tmpAction.equals("Refresh")) {
                getGenres();

            } else if(tmpAction.equals("Find")) {
                searchGenre();
            }
        }

        public void mousePressed(MouseEvent mouseEvent) {}
        public void mouseReleased(MouseEvent m) {}
        public void mouseEntered(MouseEvent mouseEvent) {}
        public void mouseExited(MouseEvent mouseEvent) {}
    }
